﻿using AnalyStock.GrapImage;
using AnalyStock.DataManage;

namespace AnalyStock.DataAnalysis;

public static class CommCalculate
{
    /// <summary>
    /// 计算算术均值
    /// </summary>
    /// <param name="inArray"></param>
    /// <param name="MA_cycle"></param>
    /// <returns></returns>
    internal static IList<MAPoint> CalculateMAVal(this IEnumerable<float> inArray, int MA_cycle)
    {
        List<MAPoint> tmpMaSeries = [];
        var _index = 0;
        inArray.ForEach(item =>
        {
            tmpMaSeries.Add(new MAPoint
            {
                Clcye = MA_cycle,
                MAIndex = _index,
                LocationIndex = _index,
                Type = "Ma",
                MAValue = _index < MA_cycle - 1
                    ? inArray.Take(_index + 1).MeanValue()
                    : inArray.Skip(_index + 1 - MA_cycle).Take(MA_cycle).MeanValue()
            });
            _index++;
        });
        return tmpMaSeries;
    }
    /// <summary>
    /// 计算移动均值
    /// </summary>
    /// <param name="inArray"></param>
    /// <param name="MA_cycle"></param>
    /// <returns></returns>
    internal static IList<MAPoint> CalculateEMAVal(this IEnumerable<float> inArray, int MA_cycle)
    {
        List<MAPoint> tmpMaSeries =
        [
            new MAPoint
            {
                Clcye = MA_cycle,
                MAIndex = 0,
                LocationIndex = 0,
                Type = "Ema",
                MAValue = inArray.First()
            }
        ];
        var _index = 1;
        inArray.Skip(1).ForEach(item =>
        {
            tmpMaSeries.Add(new MAPoint
            {
                Clcye = MA_cycle,
                MAIndex = _index,
                LocationIndex = _index,
                Type = "Ema",
                MAValue = (tmpMaSeries.Last().MAValue * (MA_cycle - 1) + item * 2.00f) / (MA_cycle + 1)
            });
            _index++;
        });
        return tmpMaSeries;
    }
    /// <summary>
    /// 计算布林线上轨、中轨、下轨值，默认20日移动均线
    /// </summary>
    /// <param name="inArray"></param>
    /// <returns></returns>
    internal static (IList<MAPoint> Up, IList<MAPoint> Mid, IList<MAPoint> Low)
        CalculateBollVal(this IEnumerable<float> inArray)
    {
        var MA_Cycle = 20;
        var bollSeries = (
            Up: new List<MAPoint>(),
            Mid: new List<MAPoint>(),
            Low: new List<MAPoint>());
        var _index = 0;
        inArray.ForEach(item =>
        {
            var closeArray = _index < MA_Cycle - 1
                ? inArray.Take(_index + 1)
                : inArray.Skip(_index + 1 - MA_Cycle).Take(MA_Cycle);
            var (mean, std) = closeArray.MeanStdValue();
            bollSeries.Mid.Add(new MAPoint
            {
                Clcye = MA_Cycle,
                MAIndex = _index,
                LocationIndex = _index,
                Type = "Mid",
                MAValue = mean,
                STDValue = std
            });
            bollSeries.Up.Add(new MAPoint
            {
                Clcye = MA_Cycle,
                MAIndex = _index,
                LocationIndex = _index,
                Type = "Up",
                MAValue = mean + (2 * std),
                STDValue = std
            });
            bollSeries.Low.Add(new MAPoint
            {
                Clcye = MA_Cycle,
                MAIndex = _index,
                LocationIndex = _index,
                Type = "Low",
                MAValue = mean - (2 * std),
                STDValue = std
            });
            _index++;
        });
        return bollSeries;
    }
    /// <summary>
    /// 计算KDJ值，默认周期参数9，3，3
    /// </summary>
    /// <param name="inHigh"></param>
    /// <param name="inLow"></param>
    /// <param name="inClose"></param>
    /// <returns></returns>
    internal static IList<KDJPoint> CalculateKDJVal(IEnumerable<float> inHigh,
        IEnumerable<float> inLow,
        IEnumerable<float> inClose)
    {
        int RSV_P = 9, K_P = 3, D_P = 3;
        List<KDJPoint> tmpKDJSeries = [];
        if (inClose.Count() < RSV_P)
        {
            return tmpKDJSeries;
        }

        float maxHigh, minLow;
        float RSV, K, D, J;
        var _index = 0;
        inClose.Skip(RSV_P - 1).ForEach(item =>
        {
            minLow = inLow.Skip(_index).Take(RSV_P).Min();
            maxHigh = inHigh.Skip(_index).Take(RSV_P).Max();
            RSV = (item - minLow) / (maxHigh - minLow) * 100.0f;
            if (_index == 0)
            {
                K = (((K_P - 1) * 50.0f) + RSV) / K_P;
                D = (((D_P - 1) * 50.0f) + K) / D_P;
                J = (3.0f * K) - (2.0f * D);
            }
            else
            {
                K = (((K_P - 1) * tmpKDJSeries.Last().KValue) + RSV) / K_P;
                D = (((D_P - 1) * tmpKDJSeries.Last().DValue) + K) / D_P;
                J = (3.0f * K) - (2.0f * D);
            }

            tmpKDJSeries.Add(new KDJPoint
            {
                KDJIndex = _index, //0开始的数据组索引
                LocationIndex = _index + RSV_P - 1, //坐标轴索引
                RSValue = RSV,
                KValue = K > 0 ? K < 100 ? K : 100 : 0,
                DValue = D > 0 ? D < 100 ? D : 100 : 0,
                JValue = J > 0 ? J < 100 ? J : 100 : 0
            });
            _index++;
        });
        return tmpKDJSeries;
    }
    /// <summary>
    /// 计算MACD值，默认周期参数12，26，9
    /// </summary>
    /// <param name="inClose"></param>
    /// <returns></returns>
    internal static IList<MACDPoint> CalculateMACDVal(this IEnumerable<float> inClose)
    {
        int Short_P = 12, Long_P = 26, DEA_P = 9;
        List<MACDPoint> macdSeries = [];
        if (!inClose.Any())
        {
            return macdSeries;
        }

        macdSeries.Add(new MACDPoint
        {
            MACDIndex = 0,
            LocationIndex = 0,
            EMAShortValue = inClose.First(),
            EMALongValue = inClose.First(),
            DIFValue = 0,
            DEAValue = 0,
            MACDValue = 0
        });
        var _index = 1;
        inClose.Skip(_index).ForEach(item =>
        {
            MACDPoint macdValue = new()
            {
                MACDIndex = _index,
                LocationIndex = _index,
                EMAShortValue = (macdSeries.Last().EMAShortValue * (Short_P - 1) + item * 2.00f) / (Short_P + 1),
                EMALongValue = (macdSeries.Last().EMALongValue * (Long_P - 1) + item * 2.00f) / (Long_P + 1)
            };
            macdValue.DIFValue = macdValue.EMAShortValue - macdValue.EMALongValue;
            macdValue.DEAValue = (macdSeries.Last().DEAValue * (DEA_P - 1) + macdValue.DIFValue * 2.00f) / (DEA_P + 1);
            macdValue.MACDValue = 2.00f * (macdValue.DIFValue - macdValue.DEAValue);
            macdSeries.Add(macdValue);
            _index++;
        });
        return macdSeries;
    }

    /// <summary>
    ///     追踪价格走势的拐点位置,价格波动大于waveRate时形成拐点
    /// </summary>
    /// <param name="waveRate">拐点临界波动率</param>
    /// <param name="inClose"></param>
    /// <returns></returns>
    internal static Dictionary<int, string> CalcIndexOnInflectPoint(this IEnumerable<float> inClose, float waveRate)
    {
        var idxInflectPoints = new Dictionary<int, string>(); //追加拐点索引
        //初始化起始追踪点的索引，值，方向                                                              
        var trackingPoint = (
            Index: 0,
            Value: inClose.First(),
            IsTrend: inClose.Skip(1).First() >= inClose.First());
        //初始点赋值
        idxInflectPoints.Add(0, "Start");
        //中间点赋值
        var _index = 0;
        inClose.ForEach(item =>
        {
            if ((item >= trackingPoint.Value) == trackingPoint.IsTrend)
            {
                trackingPoint = (_index, item, trackingPoint.IsTrend);
            }
            else
            {
                //方向不一致时，走势出现拐点，超过5%的波动后确认上一个起点为有效拐点。同时将该点确认为新的追踪点  
                if (MathF.Abs(item - trackingPoint.Value) / trackingPoint.Value >= waveRate)
                {
                    idxInflectPoints.Add(trackingPoint.Index, trackingPoint.IsTrend ? "↓" : "↑");
                    //开始新的追踪点,趋势方向改变；
                    trackingPoint = (_index, item, !trackingPoint.IsTrend);
                }
            }

            _index++;
        });
        //结束点赋值
        idxInflectPoints.Add(_index - 1, "End");
        return idxInflectPoints;
    }

    /// <summary>
    ///     描述模拟函数式，计算模拟值，导数值
    /// </summary>
    /// <param name="funcParams"></param>
    /// <param name="xVal"></param>
    /// <returns></returns>
    internal static (string FuncExpression, float FittingValue, float Derivative)
        CalcFunctionValue(double[] funcParams, int xVal)
    {
        //模型式，计算模型值，导数值
        var (FuncExpression, FittingValue, Derivative) = ("", 0.0, 0.0);
        for (var _index = 0; _index < funcParams.Length; _index++)
        {
            FuncExpression += _index is 0
                ? $"{funcParams[_index]:F2}"
                : $"{(funcParams[_index] >= 0 ? "+" : "")}{funcParams[_index]:F4}X^{_index}";
            FittingValue += funcParams[_index] * MathF.Pow(xVal, _index);
            Derivative += _index is 0 ? 0 : _index * funcParams[_index] * MathF.Pow(xVal, _index - 1);
        }

        return (FuncExpression, (float)FittingValue, (float)Derivative);
    }

    /// <summary>
    ///     价格位置的标准化值转化，位置在（0-32%，32-68%，68-100%)=>(-1,0,1)
    /// </summary>
    /// <param name="valSection"></param>
    /// <returns></returns>
    internal static (int, int, int) RevisedValOnStandard((float, float, float) valSection)
    {
        var valStandard = (
            valSection.Item1 switch { < 0.32f => -1, >= 0.68f => 1, _ => 0 },
            valSection.Item2 switch { < 0.32f => -1, >= 0.68f => 1, _ => 0 },
            valSection.Item3 switch { < 0.32f => -1, >= 0.68f => 1, _ => 0 });
        //在三个区段内，初始判断值相同时:
        //如果相邻两段的值相同，但是两者价格幅度的差距超过20%，二次修订各自趋势赋值，高的加1，低的减去1，较好匹配趋势
        if (valStandard.Item1 == valStandard.Item2
            && MathF.Abs(valSection.Item2 - valSection.Item1) is >= 0.2f)
        {
            _ = valSection.Item1 > valSection.Item2 ? valStandard.Item1 += 1 : valStandard.Item2 += 1;
        }

        if (valStandard.Item2 == valStandard.Item3
            && MathF.Abs(valSection.Item2 - valSection.Item3) is >= 0.2f)
        {
            _ = valSection.Item2 > valSection.Item3 ? valStandard.Item2 += 1 : valStandard.Item3 += 1;
        }

        //修订值超过1则进行修订
        valStandard = (
            valStandard.Item1 > 1 ? 1 : valStandard.Item1,
            valStandard.Item2 > 1 ? 1 : valStandard.Item2,
            valStandard.Item3 > 1 ? 1 : valStandard.Item3);
        return valStandard;
    }
    /// <summary>
    /// 计算交易量的换手率，参数：日交易量和流通股数
    /// </summary>
    /// <param name="inVol"></param>
    /// <param name="float_share"></param>
    /// <returns></returns>
    internal static IList<TurnOverRate> CalculateTurnOverRate(this IEnumerable<float> inVol, float float_share)
    {
        List<TurnOverRate> turnOverRates = [];
        if (float_share == 0)
        {
            return turnOverRates;
        }

        var _index = 0;
        inVol.ForEach(item =>
        {
            turnOverRates.Add(new TurnOverRate
                (_index, _index, item / (float_share * 10000.0f)));
            _index++;
        });
        return turnOverRates;
    }

    /// <summary>
    ///     计算量比，参数为当日分钟交易分钟数，若存在则计算交易分钟量比
    /// </summary>
    /// <param name="inVol"></param>
    /// <param name="TradeMintues"></param>
    /// <returns></returns>
    internal static IList<QRRatio> CalculateQRRatio(this IEnumerable<float> inVol, int TradeMintues = default)
    {
        var qRRatios = new List<QRRatio>();
        var maValue = 0.0f;
        var _index = 1;
        qRRatios.Add(new QRRatio(0, 0, 1.0f));
        inVol.Skip(_index).ForEach(item =>
        {
            maValue = _index < 4
                ? inVol.Take(_index).MeanValue()
                : inVol.Skip(_index - 5).Take(5).MeanValue();
            qRRatios.Add(new QRRatio(_index, _index, item / maValue));
            _index++;
        });
        if (TradeMintues > 0)
        {
            qRRatios.Last().QRRatioVal *= 240.0f / TradeMintues;
        }

        return qRRatios;
    }
    /// <summary>
    /// 计算PE值，参数：年度每股收益（可以根据期间预计全年值，形成动态PE）
    /// </summary>
    /// <param name="inClose"></param>
    /// <param name="YearEps"></param>
    /// <returns></returns>
    internal static IList<PEPoint> CalculatePEVal(this IEnumerable<float> inClose, float YearEps)
    {
        List<PEPoint> pePoints = [];
        if (YearEps <= 0)
        {
            return pePoints;
        }

        var _index = 0;
        inClose.ForEach(item =>
        {
            pePoints.Add(new PEPoint
            {
                LocationIndex = _index,
                EPS = YearEps,
                PE = item / YearEps
            });
            _index++;
        });
        return pePoints;
    }

    /// <summary>
    ///     计算市净率，参数是最近季度每股净资产的时点值
    /// </summary>
    /// <param name="inClose"></param>
    /// <param name="NetAssetPS"></param>
    /// <returns></returns>
    internal static IList<PBPoint> CalculatePBVal(this IEnumerable<float> inClose, float NetAssetPS)
    {
        List<PBPoint> pbPoints = [];
        if (NetAssetPS <= 0)
        {
            return pbPoints;
        }

        var _index = 0;
        inClose.ForEach(item =>
        {
            pbPoints.Add(new PBPoint
            {
                LocationIndex = _index,
                NetAssetPS = NetAssetPS,
                PB = item / NetAssetPS
            });
            _index++;
        });
        return pbPoints;
    }
}